#include <filesystem>
#include <fstream>
#include <vector>
#include <thread>

// glib header
#include <gio/gio.h>

// kysdk-ai header
#include <coreai/speech/recognizer.h>

std::vector<uint8_t> readAudioData(const std::string &filePath) {
    std::ifstream file(filePath, std::ios::binary);
    if (!file.is_open()) {
        return {};
    }

    file.seekg(0, std::ios::end);
    std::streampos fileSize = file.tellg();
    file.seekg(0, std::ios::beg);
    std::vector<uint8_t> audioData(fileSize);
    file.read(reinterpret_cast<char *>(audioData.data()), fileSize);
    return audioData;
}

void callback(SpeechRecognitionResult *result, void *user_data) {
    fprintf(stdout, "Start printing speech recognition results.\n");
    fprintf(stdout, "Speech recognition errorcode: %d\n",
            speech_recognition_result_get_error_code(result));
    fprintf(stdout, "Speech recognition error message: %s\n",
            speech_recognition_result_get_error_message(result));

    int resultType = speech_recognition_result_get_reason(result);
    const char *resultData = speech_recognition_result_get_text(result);
    fprintf(stdout, "Speech recognition result: %s\n", resultData);
    int resultErrorCode = speech_recognition_result_get_error_code(result);

    fprintf(stdout, "Printing speech recognition result completed.\n");
}

void Test_RecognitionOnce() {
    GMainLoop *pMainLoop = g_main_loop_new(nullptr, false);

    namespace fs = std::filesystem;
    fs::path dir = fs::path(__FILE__).parent_path();
    fs::path path = dir / "16k.pcm";
    if (not fs::exists(path)) {
        return;
    }
    std::vector<uint8_t> audioData = readAudioData("../testsynthesis.pcm");//path.string());

    SpeechRecognitionSession *session = speech_recognizer_create_session();

    SpeechModelConfig *modelconfig = speech_model_config_create();
    speech_model_config_set_name(modelconfig,
                                 "讯飞-语音大模型");  //或"百度-语音大模型"
    speech_model_config_set_deploy_type(modelconfig,
                                        ModelDeployType::PublicCloud);
    speech_recognizer_set_model_config(session, modelconfig);

    speech_recognizer_init_session(session);

    speech_recognizer_result_set_callback(session, callback, nullptr);

    auto *config = audio_config_create_once_audio_input_from_pcm_data(
        audioData.data(), audioData.size());

    speech_recognizer_set_audio_config(session, config);

    speech_recognizer_recognize_once_async(session);

    g_main_loop_run(pMainLoop);
    g_main_loop_unref(pMainLoop);
}

// 从麦克风进行流式语音识别
void Test01_ContinuousRecognitionFromDefaultMic() {
    SpeechRecognitionSession *session = speech_recognizer_create_session();
    speech_recognizer_init_session(session);
    speech_recognizer_result_set_callback(session, callback, nullptr);

    auto *config =
        audio_config_create_continuous_audio_input_from_default_microphone();
    speech_recognizer_set_audio_config(session, config);

    speech_recognizer_start_continuous_recognition_async(session);

    GMainLoop *pMainLoop = g_main_loop_new(nullptr, false);

    std::thread ctrlThread([pMainLoop, &config, &session] {
        while (std::getchar() != '\n') {
        }
        speech_recognizer_stop_continuous_recognition_async(session);
        audio_config_destroy(&config);
        speech_recognizer_destroy_session(&session);
        g_main_loop_quit(pMainLoop);
    });
    ctrlThread.detach();
    g_main_loop_run(pMainLoop);
    g_main_loop_unref(pMainLoop);
}

void Test03_ContinuousRecognitionFromRawData() {
    GMainLoop *pMainLoop = g_main_loop_new(nullptr, false);

    namespace fs = std::filesystem;
    fs::path dir = fs::path(__FILE__).parent_path();
    fs::path path = dir / "16k.pcm";
    if (not fs::exists(path)) {
        return;
    }
    std::vector<uint8_t> audioData = readAudioData(path.string());

    SpeechRecognitionSession *session = speech_recognizer_create_session();
    speech_recognizer_init_session(session);
    int a = 100;
    speech_recognizer_result_set_callback(session, callback, &a);

    auto *config = audio_config_create_once_audio_input_from_pcm_data(
        audioData.data(), audioData.size());

    speech_recognizer_set_audio_config(session, config);

    speech_recognizer_start_continuous_recognition_async(session);


    std::thread ctrlThread([pMainLoop, &config, &session] {
        while (std::getchar() != '\n') {
        }
        speech_recognizer_stop_continuous_recognition_async(session);
        audio_config_destroy(&config);
        speech_recognizer_destroy_session(&session);
        g_main_loop_quit(pMainLoop);
    });
    ctrlThread.detach();

    g_main_loop_run(pMainLoop);
    g_main_loop_unref(pMainLoop);
}

void Test04_ContinuousRecognitionFromStream() {
    namespace fs = std::filesystem;
    fs::path dir = fs::path(__FILE__).parent_path();
    fs::path path = dir / "16k.pcm";
    if (not fs::exists(path)) {
        return;
    }
    std::vector<uint8_t> audioData = readAudioData(path.string());

    SpeechRecognitionSession *session = speech_recognizer_create_session();
    speech_recognizer_init_session(session);
    speech_recognizer_result_set_callback(session, callback, nullptr);

    AudioDataStream *audio_data_stream = audio_data_stream_create();

    AudioConfig *config =
        audio_config_create_continuous_audio_input_from_audio_data_stream(
            audio_data_stream);

    speech_recognizer_set_audio_config(session, config);

    speech_recognizer_start_continuous_recognition_async(session);
    using namespace std::chrono_literals;

    for (int i = 0; i < audioData.size(); i += 1280) {
        std::this_thread::sleep_for(40ms);
        audio_data_stream_write(audio_data_stream, audioData.data() + i, 1280);
    }

    GMainLoop *pMainLoop = g_main_loop_new(nullptr, false);

    std::thread ctrlThread([pMainLoop, &session, &config, &audio_data_stream] {
        while (std::getchar() != '\n') {
        }

        speech_recognizer_stop_continuous_recognition_async(session);
        audio_config_destroy(&config);
        audio_data_stream_destroy(&audio_data_stream);
        speech_recognizer_destroy_session(&session);
        g_main_loop_quit(pMainLoop);
    });
    ctrlThread.detach();

    g_main_loop_run(pMainLoop);
    g_main_loop_unref(pMainLoop);
}

void Test05_ContinuousRecognitionFromFile() {
    namespace fs = std::filesystem;
    fs::path dir = fs::path(__FILE__).parent_path();
    fs::path path = dir / "16k.pcm";
    if (not fs::exists(path)) {
        return;
    }

    SpeechRecognitionSession *session = speech_recognizer_create_session();
    speech_recognizer_init_session(session);
    speech_recognizer_result_set_callback(session, callback, nullptr);

    auto *config =
        audio_config_create_once_audio_input_from_pcm_file(path.c_str());

    speech_recognizer_set_audio_config(session, config);

    speech_recognizer_start_continuous_recognition_async(session);

    GMainLoop *pMainLoop = g_main_loop_new(nullptr, false);

    std::thread ctrlThread([pMainLoop, &session, &config] {
        while (std::getchar() != '\n') {
        }

        speech_recognizer_stop_continuous_recognition_async(session);
        audio_config_destroy(&config);
        speech_recognizer_destroy_session(&session);
        g_main_loop_quit(pMainLoop);
    });
    ctrlThread.detach();

    g_main_loop_run(pMainLoop);
    g_main_loop_unref(pMainLoop);
}


int main(int argc, char *argv[])
{
    Test_RecognitionOnce();
//    Test01_ContinuousRecognitionFromDefaultMic();
//    Test03_ContinuousRecognitionFromRawData();
//    Test04_ContinuousRecognitionFromStream();
//    Test05_ContinuousRecognitionFromFile();
    return 0;
}
